﻿namespace LiSA_Server.Base.AbstractServices
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Serialization;
    using AbstractContracts;

    public static class ServiceKnownTypes
    {
        public static IEnumerable<Type> GetKnownDtoTypes(ICustomAttributeProvider provider)
        {
            return GetTypesFromAssemblies(t => t.Namespace != null && t.Namespace.Contains(".Dto."));
        }

        public static IEnumerable<Type> GetKnownCommandTypes(ICustomAttributeProvider provider)
        {
            return GetTypesFromAssemblies(t => typeof (Command).IsAssignableFrom(t));
        }

        public static IEnumerable<Type> GetKnownQueryTypes(ICustomAttributeProvider provider)
        {
            return
                GetTypesFromAssemblies(
                    t => typeof (Query).IsAssignableFrom(t) || typeof (QueryResult).IsAssignableFrom(t))
                    .Concat(new[] {typeof (Query), typeof (QueryResult)});
        }

        public static List<Type> GetTypesFromAssemblies(Func<Type, bool> criteria)
        {
            var di = new DirectoryInfo(AssemblyDirectory);
            var fis = di.GetFiles("*.dll");
            var list = new List<Type>();

            var loadedAssemblies =
                AppDomain.CurrentDomain.GetAssemblies()
                    .Where(x => x.GetName().ToString().StartsWith("LiSA_"))
                    .ToList();
            var loadedAssemblyNames = new HashSet<string>();
            foreach (var loadedAssembly in loadedAssemblies)
            {
                loadedAssemblyNames.Add(loadedAssembly.GetName().Name + ".dll");
            }

            var assemblies = new List<Assembly>();
            foreach (var fileInfo in fis)
            {
                if (!loadedAssemblyNames.Contains(fileInfo.ToString()))
                {
                    assemblies.Add(Assembly.LoadFile(fileInfo.FullName));
                }
            }

            assemblies.AddRange(loadedAssemblies);

            foreach (var currentAssembly in assemblies)
            {
                var critRes = currentAssembly.GetTypes().Where(criteria);
                foreach (var type in critRes)
                {
                    if (IsDataContract(type))
                    {
                        list.Add(type);
                    }
                }
            }

            return list;
        }

        private static bool IsDataContract(Type type)
        {
            return Attribute.IsDefined(type, typeof (DataContractAttribute));
        }

        private static string AssemblyDirectory
        {
            get
            {
                var codeBase = Assembly.GetExecutingAssembly().CodeBase;
                var uri = new UriBuilder(codeBase);
                var path = Uri.UnescapeDataString(uri.Path);
                return Path.GetDirectoryName(path);
            }
        }

        private static string GetAssemblyPath(Assembly ass)
        {
            var codeBase = ass.CodeBase;
            var uri = new UriBuilder(codeBase);
            var path = Uri.UnescapeDataString(uri.Path);

            return path;
        }
    }
}
